*	This function illustrates the use of the XFCN, Letsgo, in
*	calling a MacFortran function from HyperCard.  Letsgo does not
*	currently support callbacks from XFCNs.  If there's demand, I
*	will add that feature.

*	Letsgo can only load your function and the MacFortran runtime
*	library (f77.rl or m81.rl) dynamically.  It does not support
*	linking with the MacFortran linker.  Thus you will not be able
*	to distribute your function to others who do not own MacFortran
*	and expect them to be able to run it.

*	MacFortran will initialize the cursor to the pointer cursor when
*	it starts up.  If you find this distasteful, you can change the cursor
*	with toolbox calls from your function or one of its subroutines.

*	The usual paths are searched to locate your function and
*	runtime library.  These are: the System Folder; the folder
*	containing HyperCard (_not_ the folder containing the calling
*	HyperCard stack); and folders in the root directory called
*	"F77 Overlays" (for the runtime) and "Subroutines" (for your
*	function).  Letsgo will not launch your function if it cannot
*	find the runtime library.  If it cannot find your function,
*	it will put up the Standard GetFile dialog and ask you to find it.

*	Text files will be written to the folder containing HyperCard,
*	_not_ the folder containing the calling HyperCard stack, unless
*	you supply a path name.  

*	Please note that all data in HyperCard is contained in strings.
*	You can use formatted data reads & writes to an internal file
*	(MacFortran Reference Manual, 8.2.4) to convert values in 
*	those strings to integers or reals, etc.

*	Neither strings longer than 1k (1024) bytes nor combinations
*	of strings longer than 1k should be written or read using
*	formatted i/o.  Likewise, strings longer than 1k bytes should
*	not be passed to MacFortran's internal functions which 
*	manipulate strings, such as len.  MacFortran will crash if this
*	is done. There is a routine called by this function which 
*	illustrates determination of the length of a string of any
*	size.  Likewise, there is a routine which will read in strings
*	of arbitrary size from internal files.

*	Each argument passed to the function can be up to 32k bytes
*	in length.  The length of the argument depends on the length of 
*	the corresponding container in HyperCard.  Containers in the
*	current version of HyperCard (1.2.2) cannot be larger than
*	32 k.

*	It is not necessary to assign values to the function result.
*	The result container has been filled with spaces before being passed to
*	you.   Containers passed to Letsgo are not modified in HyperCard,
*	regardless of what you do to them.

*	The last character in a HyperCard container is zero, a byte 
*	with no bits set, the null character.  The lengths of the arguments
*	passed to your function do not include this last byte.  You can 
*	shorten the result passed back to HyperCard by stuffing zero in a 
*	byte earlier than the last byte.  This does not appear to be
*	necessary unless you have written additional data into the result
*	container  If you write data past the end of an argument, you will
*	damage HyperCard's heap, possibly with disastrous results 
*	(i.e. total corruption of the stack you're working in).

*	Thus far I have not crashed a stack by bombing while in an
*	XFCN, which has occurred maybe once or twice (he-he).
*	HyperCard seems to insulate itself fairly well from XFCNs.  The most
*	common causes of bombs are argument mismatches in type and number.

*	HyperCard (at least in my 1 meg machine) only leaves about 32k
*	of room on the stack.  That is the very maximum of room you will
*	have for local variables.  It would be safer to use < 25k. You can
*	crash quite easily if you have a heap-stack collision. Letsgo itself
*	uses about 200 bytes of stack space. If you need more space, put your
*	variables in a common block or use the "save" statement.  Then they 
*	will be allocated on the heap instead of the stack.  Named common 
*	blocks will be disposed of when the subroutine or function which
*	allocated the block ends; thus, in tight memory situations, you can
*	dispose of large arrays used in subroutines by putting them in named 
*	common blocks instead of blank common.  You can increase the heap
*	space by changing the "application memory size" of HyperCard in
*	its info box in the Finder.

*	The console window may be called up by setting a flag in Letsgo (make
*	the first argument positive).  You have to CLOSEWINDOW (_not_ 
*	DISPOSEWINDOW) the FRONTWINDOW before returning to Hypercard if you
*	call up the console window.  If you don't, HyperCard will be very
*	unstable.  The console window is suppressed if the first argument
*	is negative.

*	If your routine generates an exception which MacFortran can handle,
*	such as divide by zero, MacFortran will write the error message to
*	the front window.  When your program hangs, you have to hit the return
*	key to resume execution, if that's possible.  If your routine generates an
*	exception when the console window is not open and you don't see the error
*	message, try calling up your routine several more times to try to spot the
*	error message before activating the console window.

*	The debugger is not available.  You can call your function from a 
*	Fortran main program to use the debugger.

*	HyperTalk supports three types of array elements:
*	  words, delimited by spaces (ascii 32);
*	  items, delimited by commas (ascii 44);
*	  lines, delimited by carriage returns (ascii 13).
*	These can be combined to support three-dimensional arrays.
*	You could add other array elements for numeric arrays using
*	other non-numeric ascii character delimiters, thereby increasing
*	the possible number of dimensions.

*	MacFortran's formatted i/o to internal files with list-directed
*	editing (Fmt=*	) separates output records by carriage returns in
*	implied do loops.  Input records under the same conditions need
*	to be separated by spaces.  Thus the easiest HyperCard form for 
*	one-dimensional input arrays is a set of words.  Output arrays are
*	most easily output as a set of lines.  Remember to not use formatted
*	i/o for strings longer than 1024 bytes.  I do not currently know
*	how otherwise to access separate records in internal files using
*	read and write statements; it undoubtedly can be done by someone
*	more knowledgable about Fortran than I.  A method based on pointer
*	manipulation is illustrated in the following function.  The method 
*	also illustrates how to convert items to words, etc.  I also have 
*	written an assembly routine which does this located in FiveXFCN.bin
*	in dl 11 of the HyperCard forum of MAUG on CompuServe.

*	This function illustrates reading data from a one-dimensional
*	array of words into a real*8 variable. The data is massaged
*	a bit and spat back to HyperCard in the function result.  The
*	function also illustrates two methods to determine the length
*	of an argument, and an alternative method for inputting data. 
*	Finally, the function writes some data to a text file.
	
        character*(*) function test(a1)
	
	character 	a1*(*)

	integer*4 	i,j
	integer*4 	lena1
	real*8		temp(4)	
	
	open(10,file='testout',status='new')
	write(10,*) a1			!don't try to write uninitialized 
					!character vars!
					!don't try to write vars longer
					!than 1k!
					
* two methods for getting the length of a variable
	lena1 = longle(a1)		!method 1
	write(10,*) lena1
					!method 2
	lena1 = len(a1)			!don't use this method with
	write(10,*) lena1		!arguments longer than 1k!
	
* read in some data, massage, and return
	read(a1,*) (temp(i),i=1,3,1)	!read from an internal file
	temp(4) = 0
	do (i=1,3,1)
	  temp(4) = temp(4) + temp(i)
	repeat
	write(test,*) (temp(i),i=1,4,1) !write to an internal file
	write(10,*) a1, (temp(i),i=1,4,1)	!write to disk
	
* alternative method to read in data
	call input(a1,temp)
	write(10,*) (temp(i),i=1,3,1)
	
	return
	
	end


*Function to return the length of a HyperCard container

*n.b. This method works only with HyperCard containers

	integer*4 function longle(a1)
	
	implicit none
	
	integer*4 ptra1,i
        integer PTR,toolbx
        parameter (PTR=z'c0000000')
	
	ptra1 = toolbx(PTR,a1)

	i = 0
	do
	  if (byte(ptra1 + i) = 0) exit		!don't count the tailing zero
	  i=i+1
	repeat
	longle = i
	
	return
	end
	
* convert a1 to items and read three real*8 nums from it

* conceptually, this pointer arithmatic is the same as treating
* each character in the string as a separate element of an array
	  
	subroutine input(a1,temp)
	
	implicit none
	
	real*8 temp(4)
	integer*4 ptra1,i,j,PTR,toolbx
	integer*1 bytie	
	character a1*(*),itemelement(50)*1, item*50
	
	equivalence (itemelement,item)
	
        parameter (PTR=z'c0000000')
	
	ptra1 = toolbx(PTR,a1)

*convert a1 from words to items for the hell of it
	i = 0
	do (60 times)
	  if (byte(ptra1 + i) = 32) byte(ptra1 +i) = 44
	  i=i+1
	repeat
	
*read three real*8 items from a1
	i=1
	j=1
	do
	  item = ' '
	  do
	    bytie = byte(ptra1+i-1)
	    if (bytie = 44 .or. bytie = 0) exit	!end of item or container
	    itemelement(i) = bytie
	    i = i+1
	  repeat
	  read(item,*) temp(j)
	  i=i+1
	  j=j+1
	  if (i=2) j=j-1			!delete empty items
	  if (bytie = 0 .or. j=3) exit	     !end of container or 3 items read
	repeat
	
	return
	end
	  

